/*********************************************************************************
 * Copyright: (C) 2018 LingYun IoT Studio
 * All rights reserved.
 *
 * Filename: socket_client_multi.c
 * Description: This file is a socket client sample source code that creates multiple
 * clients to connect to the socket server and send to/receive from it continuously.
 * 
 * Version: 1.0.0(10/23/2018)
 * Author: Guo Wenxue <guowenxue@gmail.com>
 * ChangeLog: 1, Release initial version on "2018-10-23 01:38:08 PM"
 * 
 ********************************************************************************/
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>

// 服务器IP地址和端口
#define SERVER_IP "127.0.0.1"
#define SERVER_PORT 8889

// 要发送的消息
#define MSG_STR "Hello, Unix Network Program World!"

// 默认客户端数量和连接间隔（秒）
#define DEFAULT_NUM_CLIENTS 10
#define DEFAULT_DELAY_SEC 1

// 每个客户端的信息结构体
typedef struct {
    int client_id;   // 客户端编号
    int delay_sec;    // 每次连接之间的延迟时间（秒）
} client_info_t;

// 每个客户端线程的执行函数
void *client_thread_func(void *arg)
{
    client_info_t *info = (client_info_t *)arg;
    int conn_fd = -1;
    int rv = -1;
    char buf[1024];
    struct sockaddr_in serv_addr;

    while(1)
    {
        // 创建套接字
        conn_fd = socket(AF_INET, SOCK_STREAM, 0);
        if(conn_fd < 0)
        {
            printf("Client %d: 创建套接字失败: %s\n", info->client_id, strerror(errno));
            sleep(info->delay_sec); // 延时后重试
            continue;
        }

        // 设置服务器地址结构
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(SERVER_PORT);
        if(inet_aton(SERVER_IP, &serv_addr.sin_addr) == 0)
        {
            printf("Client %d: 无效的服务器IP地址\n", info->client_id);
            close(conn_fd);
            sleep(info->delay_sec); // 延时后重试
            continue;
        }

        // 连接到服务器
        if( connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
        {
            printf("Client %d: 连接到服务器 [%s:%d] 失败: %s\n", 
                info->client_id, SERVER_IP, SERVER_PORT, strerror(errno));
            close(conn_fd);
            sleep(info->delay_sec); // 延时后重试
            continue;
        }

        // 发送消息到服务器
        if( write(conn_fd, MSG_STR, strlen(MSG_STR)) < 0 )
        {
            printf("Client %d: 向服务器 [%s:%d] 发送数据失败: %s\n", 
                info->client_id, SERVER_IP, SERVER_PORT, strerror(errno));
            close(conn_fd);
            sleep(info->delay_sec); // 延时后重试
            continue;
        }

        // 从服务器读取响应
        memset(buf, 0, sizeof(buf));
        rv = read(conn_fd, buf, sizeof(buf));
        if(rv < 0)
        {
            printf("Client %d: 从服务器读取数据失败: %s\n", info->client_id, strerror(errno));
            close(conn_fd);
            sleep(info->delay_sec); // 延时后重试
            continue;
        }
        else if(rv == 0)
        {
            printf("Client %d: 服务器关闭了连接\n", info->client_id);
            close(conn_fd);
            sleep(info->delay_sec); // 延时后重试
            continue;
        }

        printf("Client %d: 从服务器读取到 %d 字节数据: '%s'\n", info->client_id, rv, buf);

        // 关闭连接
        close(conn_fd);

        // 等待一段时间后再次连接
        sleep(info->delay_sec);
    }

    return NULL;
}

int main(int argc, char **argv)
{
    int num_clients = DEFAULT_NUM_CLIENTS; // 客户端数量
    int delay_sec = DEFAULT_DELAY_SEC;      // 连接间隔时间
    int i;
    pthread_t *threads;
    client_info_t *client_info;

    // 解析命令行参数（如果有的话）
    if(argc >= 2)
    {
        num_clients = atoi(argv[1]);
        if(num_clients <= 0)
        {
            fprintf(stderr, "无效的客户端数量: %s\n", argv[1]);
            return -1;
        }
    }

    if(argc >=3 )
    {
        delay_sec = atoi(argv[2]);
        if(delay_sec < 0)
        {
            fprintf(stderr, "无效的延迟时间（秒）: %s\n", argv[2]);
            return -1;
        }
    }

    printf("启动 %d 个客户端线程，每次连接间隔 %d 秒。\n", num_clients, delay_sec);

    // 为线程和客户端信息分配内存
    threads = malloc(sizeof(pthread_t) * num_clients);
    if(!threads)
    {
        perror("分配线程内存失败");
        return -1;
    }

    client_info = malloc(sizeof(client_info_t) * num_clients);
    if(!client_info)
    {
        perror("分配客户端信息内存失败");
        free(threads);
        return -1;
    }

    // 创建客户端线程
    for(i = 0; i < num_clients; i++)
    {
        client_info[i].client_id = i + 1;
        client_info[i].delay_sec = delay_sec;

        if(pthread_create(&threads[i], NULL, client_thread_func, &client_info[i]) != 0)
        {
            printf("创建客户端线程 %d 失败: %s\n", i+1, strerror(errno));
        }
    }

    // 等待客户端线程结束（在此示例中，线程是无限循环的，因此主线程将一直等待）
    for(i = 0; i < num_clients; i++)
    {
        pthread_join(threads[i], NULL);
    }

    // 释放分配的内存（此处不可达，但作为良好习惯保留）
    free(threads);
    free(client_info);

    return 0;
}

